﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEditor;
using UnityEngine.UI;

public class ScoreViewController:MonoBehaviour
{
    public FM_Score scoreView;
    public ScoreCanvas scoreCanvas;
    FM_ScorePlayer player;

     public void Init(int width,int height)
    {
        //scoreView = new FM_Score();
        scoreView.Init(width,height);

        //scoreView.setBackgroundColor(new Color(224, 211, 175));
        //scoreView.setColor(new Color(26, 28, 33));
        scoreView.SetDistanceBetweenStaveLines(6);//设置五线之间距离
        scoreView.SetDistanceBetweenStaves(7);//设置双轨中两轨之间距离
        scoreView.SetDistanceBetweenRows(11);//设置轨道之间的距离
        scoreView.SetPaddingE(5);
        scoreView.SetPaddingS(5);
        scoreView.SetPaddingTop(150);
        scoreView.SetFirstStaveClef(FM_ClefValue.TREBLE);
        scoreView.SetSecondStaveClef(FM_ClefValue.BASS);
        scoreView.SetTimeSignature(FM_TimeSignatureValue._4, FM_TimeSignatureValue._4);
        scoreView.SetKeySignature(FM_KeySignatureValue.DOb);
        scoreView.SetCenterVertical(false);
        scoreView.SetStartBar(true);
        scoreView.SetEndBar(true);
        scoreView.SetMultiRow(true);
        scoreView.SetAllowZoomPan(true);
        scoreView.SetShowBrace(true);
        scoreView.SetTrimLastRow(true);
        scoreView.SetNotesAlign(FM_Align.ALIGN_LEFT_LAST_MEASURE);
        scoreView.ClearStaveNotes();
        //scoreView.SetVisibility(View.VISIBLE);
        scoreView.SetNoteSpacing(15);

        player = FM_ScorePlayer.GetInstance();
    }

    void ShowScore(FM_Score s, string filePath, bool showRecordTrack)
    {
        var obj = LoadJson(LoadAssetFile(filePath));

        if (obj != null)
        {
            s.SetShowRecordTrack(showRecordTrack);
            s.LoadFromJson(obj);
            player.LoadFromScore(s, 80);
            player.SetShowProgress(true);
        }
        else
        {
            Debug.LogError("ShowScore: json obj is null filePath:" + filePath);
        }
        s.DoDraw(scoreCanvas);
    }

    string curShowFile;

    public void ShowScore(string filePath, bool showRecordTrack)
    {
        curShowFile = filePath;
        if (scoreView != null)
        {
            ClearRecordNotes();
            ShowScore(scoreView, filePath, showRecordTrack);
        }
        else
        {
            Debug.LogError("ShowScore -> scoreView is null");
        }
    }

    private void ClearRecordNotes()
    {
        scoreView.ClearDynamicNotes();
    }

    public void Play(bool mute, Action onPlayEnd)
    {
        if (player != null)
        {
            player.Play(scoreCanvas, mute, onPlayEnd);
        }
        else
        {
            Debug.LogError("Play->Player is null");
        }
    }

    public void Stop()
    {
        if (player != null)
        {
            player.StopPlaying();
        }
        else
        {
            Debug.LogError("Stop->Player is null");
        }
        scoreView.UpdateCursorShow(scoreCanvas);
        scoreView.UpdateSignShow(scoreCanvas);
    }

    public void UpdateShowRecordTrack(bool showRecordTrack)
    {
        ShowScore( curShowFile, showRecordTrack);
    }

    public void AddRecordNote(float pitch)
    {
        if (scoreView.progressBar < 0)
        {
            // Log.e(TAG, "AddNote invalid progressBar:" + scoreView.progressBar);
            return;
        }

        FM_ClefValue clef = scoreView.ScoreBase.FirstStaveClef;
        FM_Note newNote = NewNoteWithPitch(clef, pitch);

        if (scoreView.showRecordTrack)
            this.scoreView.AddDynamicNote(newNote, pitch);

        int checkProgressCount = 3;
        if (scoreView.GetStaveCount() == FM_StaveCount._2)
            checkProgressCount *= 2;

        int begin = scoreView.progressBar + 1;
        int end = scoreView.progressBar - checkProgressCount * 2;

        int checkedCount = 0;
        for (int i = begin; i >= end; i--)
        {
            if (i < 0 || i >= scoreView.ScoreBase.StaveNotes.Count)
                continue;

            FM_BaseNote curNote = scoreView.ScoreBase.StaveNotes[i];
            if (curNote is FM_Chord)
            {
                FM_Chord chord = (FM_Chord)curNote;

                bool checkedNote = false;
                foreach (FM_BaseNote curFMNote in chord.Notes)
                {
                    if (curFMNote is FM_Note)
                    {
                        FM_Note fmNote = (FM_Note)curFMNote;
                        if (fmNote != null)
                        {
                            if (fmNote.isTieEnd)
                                continue;
                            else if (fmNote.note == 0 && fmNote.octave == 0 && fmNote.GetAccidental() == 0)
                                continue;
                        }
                        bool right = GetKeyWithNote(newNote) == GetKeyWithNote(curFMNote);
                        if (right)
                        {
                            scoreView.AddSign(i, true, curFMNote.GetStave());
                        }
                        Console.WriteLine(scoreView.progressBar + "check Note: " + GetNoteStr(newNote) + " with progressbar:" + i + GetNoteStr(curFMNote) + "  equal:" + right);

                        if (!checkedNote)
                        {
                            checkedNote = true;
                            checkedCount++;
                        }
                    }
                }
                if (checkedCount >= checkProgressCount)
                    break;
            }
        }
    }

    string GetNoteStr(FM_BaseNote newNote)
    {
        return "[note:" + newNote.note + "  oct:" + newNote.octave + " acc:" + newNote.GetAccidental() + "]";
    }

    public static int GetKeyWithNote(FM_BaseNote note)
    {
        return GetKeyWithNote(note.note, note.octave, note.GetAccidental());
    }

    public static int GetKeyWithNote(int note, int oct, int acc)
    {
        int key = 4 + (oct - 1) * 12 + NoteNAccidental2Index(note, acc);
        return key;
    }

    public static int[] GetNoteWithFrequency(FM_ClefValue clef, float frequency)
    {
        int midiNote = Frequency2Note(frequency);
        midiNote += 8;

        int[] output = new int[3];
        int index = midiNote % 12;

        int[] notesNAcc = Index2NoteNAccidental(index);
        output[0] = notesNAcc[0];
        output[1] = midiNote / 12;
        output[2] = notesNAcc[1];

        return output;
    }

    public static int[] Index2NoteNAccidental(int index)
    {
        int[] output = new int[2];

        if (index == 0) { output[0] = 0; output[1] = 0; }
        if (index == 1) { output[0] = 0; output[1] = FM_Accidental.Sharp; }
        if (index == 2) { output[0] = 1; output[1] = 0; }
        if (index == 3) { output[0] = 1; output[1] = FM_Accidental.Sharp; }
        if (index == 4) { output[0] = 2; output[1] = 0; }
        if (index == 5) { output[0] = 3; output[1] = 0; }
        if (index == 6) { output[0] = 3; output[1] = FM_Accidental.Sharp; }
        if (index == 7) { output[0] = 4; output[1] = 0; }
        if (index == 8) { output[0] = 4; output[1] = FM_Accidental.Sharp; }
        if (index == 9) { output[0] = 5; output[1] = 0; }
        if (index == 10) { output[0] = 5; output[1] = FM_Accidental.Sharp; }
        if (index == 11) { output[0] = 6; output[1] = 0; }

        return output;
    }

    public static int NoteNAccidental2Index(int note, int accidental)
    {
        int Y1 = FM_NoteValue.DO;
        int Y2 = FM_NoteValue.RE;
        int Y3 = FM_NoteValue.MI;
        int Y4 = FM_NoteValue.FA;
        int Y5 = FM_NoteValue.SOL;
        int Y6 = FM_NoteValue.LA;
        int Y7 = FM_NoteValue.SI;

        if (note == Y1 && accidental == FM_Accidental.None) return 0;
        if (note == Y7 && accidental == FM_Accidental.Sharp) return 0;
        if (note == Y6 && accidental == FM_Accidental.TripleSharp) return 0;
        if (note == Y2 && accidental == FM_Accidental.DoubleFlat) return 0;

        if (note == Y1 && accidental == FM_Accidental.Sharp) return 1;
        if (note == Y7 && accidental == FM_Accidental.DoubleSharp) return 1;
        if (note == Y2 && accidental == FM_Accidental.Flat) return 1;
        if (note == Y3 && accidental == FM_Accidental.TripleFlat) return 1;

        if (note == Y2 && accidental == FM_Accidental.None) return 2;
        if (note == Y1 && accidental == FM_Accidental.DoubleSharp) return 2;
        if (note == Y7 && accidental == FM_Accidental.TripleSharp) return 2;
        if (note == Y3 && accidental == FM_Accidental.DoubleFlat) return 2;
        if (note == Y4 && accidental == FM_Accidental.TripleFlat) return 2;

        if (note == Y2 && accidental == FM_Accidental.Sharp) return 3;
        if (note == Y1 && accidental == FM_Accidental.TripleSharp) return 3;
        if (note == Y3 && accidental == FM_Accidental.Flat) return 3;
        if (note == Y4 && accidental == FM_Accidental.DoubleFlat) return 3;

        if (note == Y3 && accidental == FM_Accidental.None) return 4;
        if (note == Y2 && accidental == FM_Accidental.DoubleSharp) return 4;
        if (note == Y4 && accidental == FM_Accidental.Flat) return 4;
        if (note == Y5 && accidental == FM_Accidental.TripleFlat) return 4;

        if (note == Y4 && accidental == FM_Accidental.None) return 5;
        if (note == Y3 && accidental == FM_Accidental.Sharp) return 5;
        if (note == Y2 && accidental == FM_Accidental.TripleSharp) return 5;
        if (note == Y5 && accidental == FM_Accidental.DoubleFlat) return 5;

        if (note == Y4 && accidental == FM_Accidental.Sharp) return 6;
        if (note == Y3 && accidental == FM_Accidental.DoubleSharp) return 6;
        if (note == Y5 && accidental == FM_Accidental.Flat) return 6;
        if (note == Y6 && accidental == FM_Accidental.TripleFlat) return 6;

        if (note == Y5 && accidental == FM_Accidental.None) return 7;
        if (note == Y4 && accidental == FM_Accidental.DoubleSharp) return 7;
        if (note == Y3 && accidental == FM_Accidental.TripleSharp) return 7;
        if (note == Y6 && accidental == FM_Accidental.DoubleFlat) return 7;

        if (note == Y5 && accidental == FM_Accidental.Sharp) return 8;
        if (note == Y4 && accidental == FM_Accidental.TripleSharp) return 8;
        if (note == Y6 && accidental == FM_Accidental.Flat) return 8;
        if (note == Y7 && accidental == FM_Accidental.DoubleFlat) return 8;

        if (note == Y6 && accidental == FM_Accidental.None) return 9;
        if (note == Y5 && accidental == FM_Accidental.DoubleSharp) return 9;
        if (note == Y7 && accidental == FM_Accidental.Flat) return 9;
        if (note == Y1 && accidental == FM_Accidental.TripleFlat) return 9;

        if (note == Y6 && accidental == FM_Accidental.Sharp) return 10;
        if (note == Y5 && accidental == FM_Accidental.TripleSharp) return 10;
        if (note == Y7 && accidental == FM_Accidental.Flat) return 10;
        if (note == Y1 && accidental == FM_Accidental.DoubleFlat) return 10;

        if (note == Y7 && accidental == FM_Accidental.None) return 11;
        if (note == Y6 && accidental == FM_Accidental.DoubleSharp) return 11;
        if (note == Y1 && accidental == FM_Accidental.Flat) return 11;
        if (note == Y2 && accidental == FM_Accidental.TripleFlat) return 11;

        return 0;
    }

    public static int Frequency2Note(float frequency)
    {
        double standardFrequency = 440.0;
        double logResult = Math.Log(frequency / (standardFrequency / 32.0)) / Math.Log(2);
        double note = 12.0 * logResult + 9.0 - 20;
        int output = (int)(note + 0.5f);
        // Log.d(TAG, "频率 " + frequency+ "Hz 对应的音高为 " + note);
        return output;
    }

    public static float Note2Frequency(int note)
    {
        double standardFrequency = 440.0;
        double frequency = (standardFrequency / 32.0) * Math.Pow(2, (note + 20 - 9.0) / 12.0);
        Console.WriteLine("音高 " + note + " 对应的频率为 " + frequency + " Hz");
        return (float)frequency;
    }

    FM_Note NewNoteWithPitch(FM_ClefValue clef, float pitch)
    {
        int[] notes = GetNoteWithFrequency(clef, pitch);
        int Note = notes[0];
        int Octave = notes[1];
        int Accidental = notes[2];
        int key = Octave * 12 + NoteNAccidental2Index(Note, Accidental);
        FM_DurationValue Duration = FM_DurationValue.NOTE_EIGHTH;
        int voice = 0;
        bool StemUp = true;
        FM_Note n = new FM_Note(this.scoreView.dynamicScore, Note, Octave, clef, Accidental, Duration, voice, StemUp);
        return n;
    }
    
    //[SuppressLint("WrongConstant")]
    public void OnClickBBox()
    {
        if (scoreView != null)
        {
            FM_BoundingBoxType bbIndex = scoreView.GetShowBoundingBoxes();
            bbIndex = bbIndex + 1;
            if ((int)bbIndex > 2) bbIndex = 0;
            scoreView.ShowBoundingBoxes(bbIndex);
            scoreView.SetAllowZoomControls(!scoreView.IsAllowZoomControls());
        }
    }


    public string LoadAssetFile(string path)
    {
        path = System.IO.Path.GetFileNameWithoutExtension(path);
        var t = Resources.Load<TextAsset>(path);
        if(t == null)
        {
            Debug.LogError("LoadAssetFile error:" + path);
            return "";
        }
        return t.text;
    }

    public JObject LoadJson(string text)
    {
        try
        {
            return JsonConvert.DeserializeObject<JObject>(text);
        }
        catch (Exception ex)
        {
            Debug.LogError("LoadJson: " + ex);
            return null;
        }
    }

    public Text testText;
    [ContextMenu("TestShowNote")]
    void TestShowNote()
    {
        testText.text = FM_Const.BassClef;
    }
}


